home *** CD-ROM | disk | FTP | other *** search
/ Aminet 52 / Aminet 52 (2002)(GTI - Schatztruhe)[!][Dec 2002].iso / Aminet / dev / gg / ncurses-5.3.lha / ncurses-5.3 / progs / infocmp.c < prev    next >
C/C++ Source or Header  |  2002-10-24  |  40KB  |  1,509 lines

  1. /****************************************************************************
  2.  * Copyright (c) 1998-2001,2002 Free Software Foundation, Inc.              *
  3.  *                                                                          *
  4.  * Permission is hereby granted, free of charge, to any person obtaining a  *
  5.  * copy of this software and associated documentation files (the            *
  6.  * "Software"), to deal in the Software without restriction, including      *
  7.  * without limitation the rights to use, copy, modify, merge, publish,      *
  8.  * distribute, distribute with modifications, sublicense, and/or sell       *
  9.  * copies of the Software, and to permit persons to whom the Software is    *
  10.  * furnished to do so, subject to the following conditions:                 *
  11.  *                                                                          *
  12.  * The above copyright notice and this permission notice shall be included  *
  13.  * in all copies or substantial portions of the Software.                   *
  14.  *                                                                          *
  15.  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
  16.  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
  17.  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
  18.  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
  19.  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
  20.  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
  21.  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
  22.  *                                                                          *
  23.  * Except as contained in this notice, the name(s) of the above copyright   *
  24.  * holders shall not be used in advertising or otherwise to promote the     *
  25.  * sale, use or other dealings in this Software without prior written       *
  26.  * authorization.                                                           *
  27.  ****************************************************************************/
  28.  
  29. /****************************************************************************
  30.  *  Author: Zeyd M. Ben-Halim <zmbenhal@netcom.com> 1992,1995               *
  31.  *     and: Eric S. Raymond <esr@snark.thyrsus.com>                         *
  32.  ****************************************************************************/
  33.  
  34. /*
  35.  *    infocmp.c -- decompile an entry, or compare two entries
  36.  *        written by Eric S. Raymond
  37.  */
  38.  
  39. #include <progs.priv.h>
  40.  
  41. #include <term_entry.h>
  42. #include <dump_entry.h>
  43.  
  44. MODULE_ID("$Id: infocmp.c,v 1.68 2002/10/06 01:13:04 tom Exp $")
  45.  
  46. #define L_CURL "{"
  47. #define R_CURL "}"
  48.  
  49. #define MAXTERMS    32    /* max # terminal arguments we can handle */
  50. #define MAX_STRING    1024    /* maximum formatted string */
  51.  
  52. const char *_nc_progname = "infocmp";
  53.  
  54. typedef char path[PATH_MAX];
  55.  
  56. /***************************************************************************
  57.  *
  58.  * The following control variables, together with the contents of the
  59.  * terminfo entries, completely determine the actions of the program.
  60.  *
  61.  ***************************************************************************/
  62.  
  63. static char *tname[MAXTERMS];    /* terminal type names */
  64. static ENTRY entries[MAXTERMS];    /* terminfo entries */
  65. static int termcount;        /* count of terminal entries */
  66.  
  67. static bool limited = TRUE;    /* "-r" option is not set */
  68. static bool quiet = FALSE;
  69. static const char *bool_sep = ":";
  70. static const char *s_absent = "NULL";
  71. static const char *s_cancel = "NULL";
  72. static const char *tversion;    /* terminfo version selected */
  73. static int itrace;        /* trace flag for debugging */
  74. static int mwidth = 60;
  75. static int numbers = 0;        /* format "%'char'" to/from "%{number}" */
  76. static int outform = F_TERMINFO;    /* output format */
  77. static int sortmode;        /* sort_mode */
  78.  
  79. /* main comparison mode */
  80. static int compare;
  81. #define C_DEFAULT    0    /* don't force comparison mode */
  82. #define C_DIFFERENCE    1    /* list differences between two terminals */
  83. #define C_COMMON    2    /* list common capabilities */
  84. #define C_NAND        3    /* list capabilities in neither terminal */
  85. #define C_USEALL    4    /* generate relative use-form entry */
  86. static bool ignorepads;        /* ignore pad prefixes when diffing */
  87.  
  88. #if NO_LEAKS
  89. #undef ExitProgram
  90. static void
  91. ExitProgram(int code) GCC_NORETURN;
  92. /* prototype is to get gcc to accept the noreturn attribute */
  93.      static void
  94.        ExitProgram(int code)
  95. {
  96.     while (termcount-- > 0)
  97.     _nc_free_termtype(&entries[termcount].tterm);
  98.     _nc_leaks_dump_entry();
  99.     _nc_free_and_exit(code);
  100. }
  101. #endif
  102.  
  103. static char *
  104. canonical_name(char *ptr, char *buf)
  105. /* extract the terminal type's primary name */
  106. {
  107.     char *bp;
  108.  
  109.     (void) strcpy(buf, ptr);
  110.     if ((bp = strchr(buf, '|')) != 0)
  111.     *bp = '\0';
  112.  
  113.     return (buf);
  114. }
  115.  
  116. /***************************************************************************
  117.  *
  118.  * Predicates for dump function
  119.  *
  120.  ***************************************************************************/
  121.  
  122. static int
  123. capcmp(int idx, const char *s, const char *t)
  124. /* capability comparison function */
  125. {
  126.     if (!VALID_STRING(s) && !VALID_STRING(t))
  127.     return (s != t);
  128.     else if (!VALID_STRING(s) || !VALID_STRING(t))
  129.     return (1);
  130.  
  131.     if ((idx == acs_chars_index) || !ignorepads)
  132.     return (strcmp(s, t));
  133.     else
  134.     return (_nc_capcmp(s, t));
  135. }
  136.  
  137. static int
  138. use_predicate(int type, int idx)
  139. /* predicate function to use for use decompilation */
  140. {
  141.     ENTRY *ep;
  142.  
  143.     switch (type) {
  144.     case BOOLEAN:
  145.     {
  146.         int is_set = FALSE;
  147.  
  148.         /*
  149.          * This assumes that multiple use entries are supposed
  150.          * to contribute the logical or of their boolean capabilities.
  151.          * This is true if we take the semantics of multiple uses to
  152.          * be 'each capability gets the first non-default value found
  153.          * in the sequence of use entries'.
  154.          *
  155.          * Note that cancelled or absent booleans are stored as FALSE,
  156.          * unlike numbers and strings, whose cancelled/absent state is
  157.          * recorded in the terminfo database.
  158.          */
  159.         for (ep = &entries[1]; ep < entries + termcount; ep++)
  160.         if (ep->tterm.Booleans[idx] == TRUE) {
  161.             is_set = entries[0].tterm.Booleans[idx];
  162.             break;
  163.         }
  164.         if (is_set != entries[0].tterm.Booleans[idx])
  165.         return (!is_set);
  166.         else
  167.         return (FAIL);
  168.     }
  169.  
  170.     case NUMBER:
  171.     {
  172.         int value = ABSENT_NUMERIC;
  173.  
  174.         /*
  175.          * We take the semantics of multiple uses to be 'each
  176.          * capability gets the first non-default value found
  177.          * in the sequence of use entries'.
  178.          */
  179.         for (ep = &entries[1]; ep < entries + termcount; ep++)
  180.         if (VALID_NUMERIC(ep->tterm.Numbers[idx])) {
  181.             value = ep->tterm.Numbers[idx];
  182.             break;
  183.         }
  184.  
  185.         if (value != entries[0].tterm.Numbers[idx])
  186.         return (value != ABSENT_NUMERIC);
  187.         else
  188.         return (FAIL);
  189.     }
  190.  
  191.     case STRING:
  192.     {
  193.         char *termstr, *usestr = ABSENT_STRING;
  194.  
  195.         termstr = entries[0].tterm.Strings[idx];
  196.  
  197.         /*
  198.          * We take the semantics of multiple uses to be 'each
  199.          * capability gets the first non-default value found
  200.          * in the sequence of use entries'.
  201.          */
  202.         for (ep = &entries[1]; ep < entries + termcount; ep++)
  203.         if (ep->tterm.Strings[idx]) {
  204.             usestr = ep->tterm.Strings[idx];
  205.             break;
  206.         }
  207.  
  208.         if (usestr == ABSENT_STRING && termstr == ABSENT_STRING)
  209.         return (FAIL);
  210.         else if (!usestr || !termstr || capcmp(idx, usestr, termstr))
  211.         return (TRUE);
  212.         else
  213.         return (FAIL);
  214.     }
  215.     }
  216.  
  217.     return (FALSE);        /* pacify compiler */
  218. }
  219.  
  220. static bool
  221. useeq(ENTRY * e1, ENTRY * e2)
  222. /* are the use references in two entries equivalent? */
  223. {
  224.     int i, j;
  225.  
  226.     if (e1->nuses != e2->nuses)
  227.     return (FALSE);
  228.  
  229.     /* Ugh...this is quadratic again */
  230.     for (i = 0; i < e1->nuses; i++) {
  231.     bool foundmatch = FALSE;
  232.  
  233.     /* search second entry for given use reference */
  234.     for (j = 0; j < e2->nuses; j++)
  235.         if (!strcmp(e1->uses[i].name, e2->uses[j].name)) {
  236.         foundmatch = TRUE;
  237.         break;
  238.         }
  239.  
  240.     if (!foundmatch)
  241.         return (FALSE);
  242.     }
  243.  
  244.     return (TRUE);
  245. }
  246.  
  247. static bool
  248. entryeq(TERMTYPE * t1, TERMTYPE * t2)
  249. /* are two entries equivalent? */
  250. {
  251.     int i;
  252.  
  253.     for (i = 0; i < NUM_BOOLEANS(t1); i++)
  254.     if (t1->Booleans[i] != t2->Booleans[i])
  255.         return (FALSE);
  256.  
  257.     for (i = 0; i < NUM_NUMBERS(t1); i++)
  258.     if (t1->Numbers[i] != t2->Numbers[i])
  259.         return (FALSE);
  260.  
  261.     for (i = 0; i < NUM_STRINGS(t1); i++)
  262.     if (capcmp(i, t1->Strings[i], t2->Strings[i]))
  263.         return (FALSE);
  264.  
  265.     return (TRUE);
  266. }
  267.  
  268. #define TIC_EXPAND(result) _nc_tic_expand(result, outform==F_TERMINFO, numbers)
  269.  
  270. static void
  271. print_uses(ENTRY * ep, FILE * fp)
  272. /* print an entry's use references */
  273. {
  274.     int i;
  275.  
  276.     if (!ep->nuses)
  277.     fputs("NULL", fp);
  278.     else
  279.     for (i = 0; i < ep->nuses; i++) {
  280.         fputs(ep->uses[i].name, fp);
  281.         if (i < ep->nuses - 1)
  282.         fputs(" ", fp);
  283.     }
  284. }
  285.  
  286. static const char *
  287. dump_boolean(int val)
  288. /* display the value of a boolean capability */
  289. {
  290.     switch (val) {
  291.     case ABSENT_BOOLEAN:
  292.     return (s_absent);
  293.     case CANCELLED_BOOLEAN:
  294.     return (s_cancel);
  295.     case FALSE:
  296.     return ("F");
  297.     case TRUE:
  298.     return ("T");
  299.     default:
  300.     return ("?");
  301.     }
  302. }
  303.  
  304. static void
  305. dump_numeric(int val, char *buf)
  306. /* display the value of a boolean capability */
  307. {
  308.     switch (val) {
  309.     case ABSENT_NUMERIC:
  310.     strcpy(buf, s_absent);
  311.     break;
  312.     case CANCELLED_NUMERIC:
  313.     strcpy(buf, s_cancel);
  314.     break;
  315.     default:
  316.     sprintf(buf, "%d", val);
  317.     break;
  318.     }
  319. }
  320.  
  321. static void
  322. dump_string(char *val, char *buf)
  323. /* display the value of a string capability */
  324. {
  325.     if (val == ABSENT_STRING)
  326.     strcpy(buf, s_absent);
  327.     else if (val == CANCELLED_STRING)
  328.     strcpy(buf, s_cancel);
  329.     else {
  330.     sprintf(buf, "'%.*s'", MAX_STRING - 3, TIC_EXPAND(val));
  331.     }
  332. }
  333.  
  334. static void
  335. compare_predicate(int type, int idx, const char *name)
  336. /* predicate function to use for entry difference reports */
  337. {
  338.     register ENTRY *e1 = &entries[0];
  339.     register ENTRY *e2 = &entries[1];
  340.     char buf1[MAX_STRING], buf2[MAX_STRING];
  341.     int b1, b2;
  342.     int n1, n2;
  343.     char *s1, *s2;
  344.  
  345.     switch (type) {
  346.     case CMP_BOOLEAN:
  347.     b1 = e1->tterm.Booleans[idx];
  348.     b2 = e2->tterm.Booleans[idx];
  349.     switch (compare) {
  350.     case C_DIFFERENCE:
  351.         if (!(b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN) && b1 != b2)
  352.         (void) printf("\t%s: %s%s%s.\n",
  353.                   name,
  354.                   dump_boolean(b1),
  355.                   bool_sep,
  356.                   dump_boolean(b2));
  357.         break;
  358.  
  359.     case C_COMMON:
  360.         if (b1 == b2 && b1 != ABSENT_BOOLEAN)
  361.         (void) printf("\t%s= %s.\n", name, dump_boolean(b1));
  362.         break;
  363.  
  364.     case C_NAND:
  365.         if (b1 == ABSENT_BOOLEAN && b2 == ABSENT_BOOLEAN)
  366.         (void) printf("\t!%s.\n", name);
  367.         break;
  368.     }
  369.     break;
  370.  
  371.     case CMP_NUMBER:
  372.     n1 = e1->tterm.Numbers[idx];
  373.     n2 = e2->tterm.Numbers[idx];
  374.     dump_numeric(n1, buf1);
  375.     dump_numeric(n2, buf2);
  376.     switch (compare) {
  377.     case C_DIFFERENCE:
  378.         if (!((n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)) && n1 != n2)
  379.         (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
  380.         break;
  381.  
  382.     case C_COMMON:
  383.         if (n1 != ABSENT_NUMERIC && n2 != ABSENT_NUMERIC && n1 == n2)
  384.         (void) printf("\t%s= %s.\n", name, buf1);
  385.         break;
  386.  
  387.     case C_NAND:
  388.         if (n1 == ABSENT_NUMERIC && n2 == ABSENT_NUMERIC)
  389.         (void) printf("\t!%s.\n", name);
  390.         break;
  391.     }
  392.     break;
  393.  
  394.     case CMP_STRING:
  395.     s1 = e1->tterm.Strings[idx];
  396.     s2 = e2->tterm.Strings[idx];
  397.     switch (compare) {
  398.     case C_DIFFERENCE:
  399.         if (capcmp(idx, s1, s2)) {
  400.         dump_string(s1, buf1);
  401.         dump_string(s2, buf2);
  402.         if (strcmp(buf1, buf2))
  403.             (void) printf("\t%s: %s, %s.\n", name, buf1, buf2);
  404.         }
  405.         break;
  406.  
  407.     case C_COMMON:
  408.         if (s1 && s2 && !capcmp(idx, s1, s2))
  409.         (void) printf("\t%s= '%s'.\n", name, TIC_EXPAND(s1));
  410.         break;
  411.  
  412.     case C_NAND:
  413.         if (!s1 && !s2)
  414.         (void) printf("\t!%s.\n", name);
  415.         break;
  416.     }
  417.     break;
  418.  
  419.     case CMP_USE:
  420.     /* unlike the other modes, this compares *all* use entries */
  421.     switch (compare) {
  422.     case C_DIFFERENCE:
  423.         if (!useeq(e1, e2)) {
  424.         (void) fputs("\tuse: ", stdout);
  425.         print_uses(e1, stdout);
  426.         fputs(", ", stdout);
  427.         print_uses(e2, stdout);
  428.         fputs(".\n", stdout);
  429.         }
  430.         break;
  431.  
  432.     case C_COMMON:
  433.         if (e1->nuses && e2->nuses && useeq(e1, e2)) {
  434.         (void) fputs("\tuse: ", stdout);
  435.         print_uses(e1, stdout);
  436.         fputs(".\n", stdout);
  437.         }
  438.         break;
  439.  
  440.     case C_NAND:
  441.         if (!e1->nuses && !e2->nuses)
  442.         (void) printf("\t!use.\n");
  443.         break;
  444.     }
  445.     }
  446. }
  447.  
  448. /***************************************************************************
  449.  *
  450.  * Init string analysis
  451.  *
  452.  ***************************************************************************/
  453.  
  454. typedef struct {
  455.     const char *from;
  456.     const char *to;
  457. } assoc;
  458.  
  459. static const assoc std_caps[] =
  460. {
  461.     /* these are specified by X.364 and iBCS2 */
  462.     {"\033c", "RIS"},        /* full reset */
  463.     {"\0337", "SC"},        /* save cursor */
  464.     {"\0338", "RC"},        /* restore cursor */
  465.     {"\033[r", "RSR"},        /* not an X.364 mnemonic */
  466.     {"\033[m", "SGR0"},        /* not an X.364 mnemonic */
  467.     {"\033[2J", "ED2"},        /* clear page */
  468.  
  469.     /* this group is specified by ISO 2022 */
  470.     {"\033(0", "ISO DEC G0"},    /* enable DEC graphics for G0 */
  471.     {"\033(A", "ISO UK G0"},    /* enable UK chars for G0 */
  472.     {"\033(B", "ISO US G0"},    /* enable US chars for G0 */
  473.     {"\033)0", "ISO DEC G1"},    /* enable DEC graphics for G1 */
  474.     {"\033)A", "ISO UK G1"},    /* enable UK chars for G1 */
  475.     {"\033)B", "ISO US G1"},    /* enable US chars for G1 */
  476.  
  477.     /* these are DEC private modes widely supported by emulators */
  478.     {"\033=", "DECPAM"},    /* application keypad mode */
  479.     {"\033>", "DECPNM"},    /* normal keypad mode */
  480.     {"\033<", "DECANSI"},    /* enter ANSI mode */
  481.  
  482.     {(char *) 0, (char *) 0}
  483. };
  484.  
  485. static const assoc private_modes[] =
  486. /* DEC \E[ ... [hl] modes recognized by many emulators */
  487. {
  488.     {"1", "CKM"},        /* application cursor keys */
  489.     {"2", "ANM"},        /* set VT52 mode */
  490.     {"3", "COLM"},        /* 132-column mode */
  491.     {"4", "SCLM"},        /* smooth scroll */
  492.     {"5", "SCNM"},        /* reverse video mode */
  493.     {"6", "OM"},        /* origin mode */
  494.     {"7", "AWM"},        /* wraparound mode */
  495.     {"8", "ARM"},        /* auto-repeat mode */
  496.     {(char *) 0, (char *) 0}
  497. };
  498.  
  499. static const assoc ecma_highlights[] =
  500. /* recognize ECMA attribute sequences */
  501. {
  502.     {"0", "NORMAL"},        /* normal */
  503.     {"1", "+BOLD"},        /* bold on */
  504.     {"2", "+DIM"},        /* dim on */
  505.     {"3", "+ITALIC"},        /* italic on */
  506.     {"4", "+UNDERLINE"},    /* underline on */
  507.     {"5", "+BLINK"},        /* blink on */
  508.     {"6", "+FASTBLINK"},    /* fastblink on */
  509.     {"7", "+REVERSE"},        /* reverse on */
  510.     {"8", "+INVISIBLE"},    /* invisible on */
  511.     {"9", "+DELETED"},        /* deleted on */
  512.     {"10", "MAIN-FONT"},    /* select primary font */
  513.     {"11", "ALT-FONT-1"},    /* select alternate font 1 */
  514.     {"12", "ALT-FONT-2"},    /* select alternate font 2 */
  515.     {"13", "ALT-FONT-3"},    /* select alternate font 3 */
  516.     {"14", "ALT-FONT-4"},    /* select alternate font 4 */
  517.     {"15", "ALT-FONT-5"},    /* select alternate font 5 */
  518.     {"16", "ALT-FONT-6"},    /* select alternate font 6 */
  519.     {"17", "ALT-FONT-7"},    /* select alternate font 7 */
  520.     {"18", "ALT-FONT-1"},    /* select alternate font 1 */
  521.     {"19", "ALT-FONT-1"},    /* select alternate font 1 */
  522.     {"20", "FRAKTUR"},        /* Fraktur font */
  523.     {"21", "DOUBLEUNDER"},    /* double underline */
  524.     {"22", "-DIM"},        /* dim off */
  525.     {"23", "-ITALIC"},        /* italic off */
  526.     {"24", "-UNDERLINE"},    /* underline off */
  527.     {"25", "-BLINK"},        /* blink off */
  528.     {"26", "-FASTBLINK"},    /* fastblink off */
  529.     {"27", "-REVERSE"},        /* reverse off */
  530.     {"28", "-INVISIBLE"},    /* invisible off */
  531.     {"29", "-DELETED"},        /* deleted off */
  532.     {(char *) 0, (char *) 0}
  533. };
  534.  
  535. static void
  536. analyze_string(const char *name, const char *cap, TERMTYPE * tp)
  537. {
  538.     char buf[MAX_TERMINFO_LENGTH];
  539.     char buf2[MAX_TERMINFO_LENGTH];
  540.     const char *sp, *ep;
  541.     const assoc *ap;
  542.  
  543.     if (cap == ABSENT_STRING || cap == CANCELLED_STRING)
  544.     return;
  545.     (void) printf("%s: ", name);
  546.  
  547.     buf[0] = '\0';
  548.     for (sp = cap; *sp; sp++) {
  549.     int i;
  550.     size_t len = 0;
  551.     const char *expansion = 0;
  552.  
  553.     /* first, check other capabilities in this entry */
  554.     for (i = 0; i < STRCOUNT; i++) {
  555.         char *cp = tp->Strings[i];
  556.  
  557.         /* don't use soft-key capabilities */
  558.         if (strnames[i][0] == 'k' && strnames[i][0] == 'f')
  559.         continue;
  560.  
  561.         if (cp != ABSENT_STRING && cp != CANCELLED_STRING && cp[0] && cp
  562.         != cap) {
  563.         len = strlen(cp);
  564.         (void) strncpy(buf2, sp, len);
  565.         buf2[len] = '\0';
  566.  
  567.         if (_nc_capcmp(cp, buf2))
  568.             continue;
  569.  
  570. #define ISRS(s)    (!strncmp((s), "is", 2) || !strncmp((s), "rs", 2))
  571.         /*
  572.          * Theoretically we just passed the test for translation
  573.          * (equality once the padding is stripped).  However, there
  574.          * are a few more hoops that need to be jumped so that
  575.          * identical pairs of initialization and reset strings
  576.          * don't just refer to each other.
  577.          */
  578.         if (ISRS(name) || ISRS(strnames[i]))
  579.             if (cap < cp)
  580.             continue;
  581. #undef ISRS
  582.  
  583.         expansion = strnames[i];
  584.         break;
  585.         }
  586.     }
  587.  
  588.     /* now check the standard capabilities */
  589.     if (!expansion)
  590.         for (ap = std_caps; ap->from; ap++) {
  591.         len = strlen(ap->from);
  592.  
  593.         if (strncmp(ap->from, sp, len) == 0) {
  594.             expansion = ap->to;
  595.             break;
  596.         }
  597.         }
  598.  
  599.     /* now check for private-mode sequences */
  600.     if (!expansion
  601.         && sp[0] == '\033' && sp[1] == '[' && sp[2] == '?'
  602.         && (len = strspn(sp + 3, "0123456789;"))
  603.         && ((sp[3 + len] == 'h') || (sp[3 + len] == 'l'))) {
  604.         char buf3[MAX_TERMINFO_LENGTH];
  605.  
  606.         (void) strcpy(buf2, (sp[3 + len] == 'h') ? "DEC+" : "DEC-");
  607.         (void) strncpy(buf3, sp + 3, len);
  608.         len += 4;
  609.         buf3[len] = '\0';
  610.  
  611.         ep = strtok(buf3, ";");
  612.         do {
  613.         bool found = FALSE;
  614.  
  615.         for (ap = private_modes; ap->from; ap++) {
  616.             size_t tlen = strlen(ap->from);
  617.  
  618.             if (strncmp(ap->from, ep, tlen) == 0) {
  619.             (void) strcat(buf2, ap->to);
  620.             found = TRUE;
  621.             break;
  622.             }
  623.         }
  624.  
  625.         if (!found)
  626.             (void) strcat(buf2, ep);
  627.         (void) strcat(buf2, ";");
  628.         } while
  629.         ((ep = strtok((char *) 0, ";")));
  630.         buf2[strlen(buf2) - 1] = '\0';
  631.         expansion = buf2;
  632.     }
  633.  
  634.     /* now check for ECMA highlight sequences */
  635.     if (!expansion
  636.         && sp[0] == '\033' && sp[1] == '['
  637.         && (len = strspn(sp + 2, "0123456789;"))
  638.         && sp[2 + len] == 'm') {
  639.         char buf3[MAX_TERMINFO_LENGTH];
  640.  
  641.         (void) strcpy(buf2, "SGR:");
  642.         (void) strncpy(buf3, sp + 2, len);
  643.         len += 3;
  644.         buf3[len] = '\0';
  645.  
  646.         ep = strtok(buf3, ";");
  647.         do {
  648.         bool found = FALSE;
  649.  
  650.         for (ap = ecma_highlights; ap->from; ap++) {
  651.             size_t tlen = strlen(ap->from);
  652.  
  653.             if (strncmp(ap->from, ep, tlen) == 0) {
  654.             (void) strcat(buf2, ap->to);
  655.             found = TRUE;
  656.             break;
  657.             }
  658.         }
  659.  
  660.         if (!found)
  661.             (void) strcat(buf2, ep);
  662.         (void) strcat(buf2, ";");
  663.         } while
  664.         ((ep = strtok((char *) 0, ";")));
  665.  
  666.         buf2[strlen(buf2) - 1] = '\0';
  667.         expansion = buf2;
  668.     }
  669.     /* now check for scroll region reset */
  670.     if (!expansion) {
  671.         (void) sprintf(buf2, "\033[1;%dr", tp->Numbers[2]);
  672.         len = strlen(buf2);
  673.         if (strncmp(buf2, sp, len) == 0)
  674.         expansion = "RSR";
  675.     }
  676.  
  677.     /* now check for home-down */
  678.     if (!expansion) {
  679.         (void) sprintf(buf2, "\033[%d;1H", tp->Numbers[2]);
  680.         len = strlen(buf2);
  681.         if (strncmp(buf2, sp, len) == 0)
  682.         expansion = "LL";
  683.     }
  684.  
  685.     /* now look at the expansion we got, if any */
  686.     if (expansion) {
  687.         (void) sprintf(buf + strlen(buf), "{%s}", expansion);
  688.         sp += len - 1;
  689.         continue;
  690.     } else {
  691.         /* couldn't match anything */
  692.         buf2[0] = *sp;
  693.         buf2[1] = '\0';
  694.         (void) strcat(buf, TIC_EXPAND(buf2));
  695.     }
  696.     }
  697.     (void) printf("%s\n", buf);
  698. }
  699.  
  700. /***************************************************************************
  701.  *
  702.  * File comparison
  703.  *
  704.  ***************************************************************************/
  705.  
  706. static void
  707. file_comparison(int argc, char *argv[])
  708. {
  709. #define MAXCOMPARE    2
  710.     /* someday we may allow comparisons on more files */
  711.     int filecount = 0;
  712.     ENTRY *heads[MAXCOMPARE];
  713.     ENTRY *qp, *rp;
  714.     int i, n;
  715.  
  716.     dump_init((char *) 0, F_LITERAL, S_TERMINFO, 0, itrace, FALSE);
  717.  
  718.     for (n = 0; n < argc && n < MAXCOMPARE; n++) {
  719.     if (freopen(argv[n], "r", stdin) == 0)
  720.         _nc_err_abort("Can't open %s", argv[n]);
  721.  
  722.     _nc_head = _nc_tail = 0;
  723.  
  724.     /* parse entries out of the source file */
  725.     _nc_set_source(argv[n]);
  726.     _nc_read_entry_source(stdin, NULL, TRUE, FALSE, NULLHOOK);
  727.  
  728.     if (itrace)
  729.         (void) fprintf(stderr, "Resolving file %d...\n", n - 0);
  730.  
  731.     /* maybe do use resolution */
  732.     if (!_nc_resolve_uses(!limited)) {
  733.         (void) fprintf(stderr,
  734.                "There are unresolved use entries in %s:\n",
  735.                argv[n]);
  736.         for_entry_list(qp) {
  737.         if (qp->nuses) {
  738.             (void) fputs(qp->tterm.term_names, stderr);
  739.             (void) fputc('\n', stderr);
  740.         }
  741.         }
  742.         exit(EXIT_FAILURE);
  743.     }
  744.  
  745.     heads[filecount] = _nc_head;
  746.     filecount++;
  747.     }
  748.  
  749.     /* OK, all entries are in core.  Ready to do the comparison */
  750.     if (itrace)
  751.     (void) fprintf(stderr, "Entries are now in core...\n");
  752.  
  753.     /* The entry-matching loop. Sigh, this is intrinsically quadratic. */
  754.     for (qp = heads[0]; qp; qp = qp->next) {
  755.     for (rp = heads[1]; rp; rp = rp->next)
  756.         if (_nc_entry_match(qp->tterm.term_names, rp->tterm.term_names)) {
  757.         if (qp->ncrosslinks < MAX_CROSSLINKS)
  758.             qp->crosslinks[qp->ncrosslinks] = rp;
  759.         qp->ncrosslinks++;
  760.  
  761.         if (rp->ncrosslinks < MAX_CROSSLINKS)
  762.             rp->crosslinks[rp->ncrosslinks] = qp;
  763.         rp->ncrosslinks++;
  764.         }
  765.     }
  766.  
  767.     /* now we have two circular lists with crosslinks */
  768.     if (itrace)
  769.     (void) fprintf(stderr, "Name matches are done...\n");
  770.  
  771.     for (qp = heads[0]; qp; qp = qp->next) {
  772.     if (qp->ncrosslinks > 1) {
  773.         (void) fprintf(stderr,
  774.                "%s in file 1 (%s) has %d matches in file 2 (%s):\n",
  775.                _nc_first_name(qp->tterm.term_names),
  776.                argv[0],
  777.                qp->ncrosslinks,
  778.                argv[1]);
  779.         for (i = 0; i < qp->ncrosslinks; i++)
  780.         (void) fprintf(stderr,
  781.                    "\t%s\n",
  782.                    _nc_first_name((qp->crosslinks[i])->tterm.term_names));
  783.     }
  784.     }
  785.  
  786.     for (rp = heads[1]; rp; rp = rp->next) {
  787.     if (rp->ncrosslinks > 1) {
  788.         (void) fprintf(stderr,
  789.                "%s in file 2 (%s) has %d matches in file 1 (%s):\n",
  790.                _nc_first_name(rp->tterm.term_names),
  791.                argv[1],
  792.                rp->ncrosslinks,
  793.                argv[0]);
  794.         for (i = 0; i < rp->ncrosslinks; i++)
  795.         (void) fprintf(stderr,
  796.                    "\t%s\n",
  797.                    _nc_first_name((rp->crosslinks[i])->tterm.term_names));
  798.     }
  799.     }
  800.  
  801.     (void) printf("In file 1 (%s) only:\n", argv[0]);
  802.     for (qp = heads[0]; qp; qp = qp->next)
  803.     if (qp->ncrosslinks == 0)
  804.         (void) printf("\t%s\n",
  805.               _nc_first_name(qp->tterm.term_names));
  806.  
  807.     (void) printf("In file 2 (%s) only:\n", argv[1]);
  808.     for (rp = heads[1]; rp; rp = rp->next)
  809.     if (rp->ncrosslinks == 0)
  810.         (void) printf("\t%s\n",
  811.               _nc_first_name(rp->tterm.term_names));
  812.  
  813.     (void) printf("The following entries are equivalent:\n");
  814.     for (qp = heads[0]; qp; qp = qp->next) {
  815.     rp = qp->crosslinks[0];
  816.  
  817.     if (qp->ncrosslinks == 1) {
  818.         rp = qp->crosslinks[0];
  819.  
  820.         repair_acsc(&qp->tterm);
  821.         repair_acsc(&rp->tterm);
  822. #if NCURSES_XNAMES
  823.         _nc_align_termtype(&qp->tterm, &rp->tterm);
  824. #endif
  825.         if (entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp)) {
  826.         char name1[NAMESIZE], name2[NAMESIZE];
  827.  
  828.         (void) canonical_name(qp->tterm.term_names, name1);
  829.         (void) canonical_name(rp->tterm.term_names, name2);
  830.  
  831.         (void) printf("%s = %s\n", name1, name2);
  832.         }
  833.     }
  834.     }
  835.  
  836.     (void) printf("Differing entries:\n");
  837.     termcount = 2;
  838.     for (qp = heads[0]; qp; qp = qp->next) {
  839.  
  840.     if (qp->ncrosslinks == 1) {
  841.         rp = qp->crosslinks[0];
  842. #if NCURSES_XNAMES
  843.         /* sorry - we have to do this on each pass */
  844.         _nc_align_termtype(&qp->tterm, &rp->tterm);
  845. #endif
  846.         if (!(entryeq(&qp->tterm, &rp->tterm) && useeq(qp, rp))) {
  847.         char name1[NAMESIZE], name2[NAMESIZE];
  848.  
  849.         entries[0] = *qp;
  850.         entries[1] = *rp;
  851.  
  852.         (void) canonical_name(qp->tterm.term_names, name1);
  853.         (void) canonical_name(rp->tterm.term_names, name2);
  854.  
  855.         switch (compare) {
  856.         case C_DIFFERENCE:
  857.             if (itrace)
  858.             (void) fprintf(stderr,
  859.                        "infocmp: dumping differences\n");
  860.             (void) printf("comparing %s to %s.\n", name1, name2);
  861.             compare_entry(compare_predicate, &entries->tterm, quiet);
  862.             break;
  863.  
  864.         case C_COMMON:
  865.             if (itrace)
  866.             (void) fprintf(stderr,
  867.                        "infocmp: dumping common capabilities\n");
  868.             (void) printf("comparing %s to %s.\n", name1, name2);
  869.             compare_entry(compare_predicate, &entries->tterm, quiet);
  870.             break;
  871.  
  872.         case C_NAND:
  873.             if (itrace)
  874.             (void) fprintf(stderr,
  875.                        "infocmp: dumping differences\n");
  876.             (void) printf("comparing %s to %s.\n", name1, name2);
  877.             compare_entry(compare_predicate, &entries->tterm, quiet);
  878.             break;
  879.  
  880.         }
  881.         }
  882.     }
  883.     }
  884. }
  885.  
  886. static void
  887. usage(void)
  888. {
  889.     static const char *tbl[] =
  890.     {
  891.     "Usage: infocmp [options] [-A directory] [-B directory] [termname...]"
  892.     ,""
  893.     ,"Options:"
  894.     ,"  -1    print single-column"
  895.     ,"  -C    use termcap-names"
  896.     ,"  -F    compare terminfo-files"
  897.     ,"  -I    use terminfo-names"
  898.     ,"  -L    use long names"
  899.     ,"  -R subset (see manpage)"
  900.     ,"  -T    eliminate size limits (test)"
  901.     ,"  -V    print version"
  902. #if NCURSES_XNAMES
  903.     ,"  -a    with -F, list commented-out caps"
  904. #endif
  905.     ,"  -c    list common capabilities"
  906.     ,"  -d    list different capabilities"
  907.     ,"  -e    format output for C initializer"
  908.     ,"  -E    format output as C tables"
  909.     ,"  -f    with -1, format complex strings"
  910.     ,"  -G    format %{number} to %'char'"
  911.     ,"  -g    format %'char' to %{number}"
  912.     ,"  -i    analyze initialization/reset"
  913.     ,"  -l    output terminfo names"
  914.     ,"  -n    list capabilities in neither"
  915.     ,"  -p    ignore padding specifiers"
  916.     ,"  -q    brief listing, removes headers"
  917.     ,"  -r    with -C, output in termcap form"
  918.     ,"  -r    with -F, resolve use-references"
  919.     ,"  -s [d|i|l|c] sort fields"
  920. #if NCURSES_XNAMES
  921.     ,"  -t    suppress commented-out capabilities"
  922. #endif
  923.     ,"  -u    produce source with 'use='"
  924.     ,"  -v number  (verbose)"
  925.     ,"  -w number  (width)"
  926.     };
  927.     const size_t first = 3;
  928.     const size_t last = SIZEOF(tbl);
  929.     const size_t left = (last - first + 1) / 2 + first;
  930.     size_t n;
  931.  
  932.     for (n = 0; n < left; n++) {
  933.     size_t m = (n < first) ? last : n + left - first;
  934.     if (m < last)
  935.         fprintf(stderr, "%-40.40s%s\n", tbl[n], tbl[m]);
  936.     else
  937.         fprintf(stderr, "%s\n", tbl[n]);
  938.     }
  939.     exit(EXIT_FAILURE);
  940. }
  941.  
  942. static char *
  943. any_initializer(const char *fmt, const char *type)
  944. {
  945.     static char *initializer;
  946.     char *s;
  947.  
  948.     if (initializer == 0)
  949.     initializer = (char *) malloc(strlen(entries->tterm.term_names) +
  950.                       strlen(type) + strlen(fmt));
  951.  
  952.     (void) strcpy(initializer, entries->tterm.term_names);
  953.     for (s = initializer; *s != 0 && *s != '|'; s++) {
  954.     if (!isalnum(UChar(*s)))
  955.         *s = '_';
  956.     }
  957.     *s = 0;
  958.     (void) sprintf(s, fmt, type);
  959.     return initializer;
  960. }
  961.  
  962. static char *
  963. name_initializer(const char *type)
  964. {
  965.     return any_initializer("_%s_data", type);
  966. }
  967.  
  968. static char *
  969. string_variable(const char *type)
  970. {
  971.     return any_initializer("_s_%s", type);
  972. }
  973.  
  974. /* dump C initializers for the terminal type */
  975. static void
  976. dump_initializers(TERMTYPE * term)
  977. {
  978.     int n;
  979.     int size;
  980.     const char *str = 0;
  981.  
  982.     printf("\nstatic char %s[] = \"%s\";\n\n",
  983.        name_initializer("alias"), entries->tterm.term_names);
  984.  
  985.     for_each_string(n, term) {
  986.     char buf[MAX_STRING], *sp, *tp;
  987.  
  988.     if (VALID_STRING(term->Strings[n])) {
  989.         tp = buf;
  990.         *tp++ = '"';
  991.         for (sp = term->Strings[n];
  992.          *sp != 0 && (tp - buf) < MAX_STRING - 6;
  993.          sp++) {
  994.         if (isascii(UChar(*sp))
  995.             && isprint(UChar(*sp))
  996.             && *sp != '\\'
  997.             && *sp != '"')
  998.             *tp++ = *sp;
  999.         else {
  1000.             (void) sprintf(tp, "\\%03o", UChar(*sp));
  1001.             tp += 4;
  1002.         }
  1003.         }
  1004.         *tp++ = '"';
  1005.         *tp = '\0';
  1006.         size += (strlen(term->Strings[n]) + 1);
  1007.         (void) printf("static char %-20s[] = %s;\n",
  1008.               string_variable(ExtStrname(term, n, strnames)), buf);
  1009.     }
  1010.     }
  1011.     printf("\n");
  1012.  
  1013.     (void) printf("static char %s[] = %s\n", name_initializer("bool"), L_CURL);
  1014.  
  1015.     for_each_boolean(n, term) {
  1016.     switch ((int) (term->Booleans[n])) {
  1017.     case TRUE:
  1018.         str = "TRUE";
  1019.         break;
  1020.  
  1021.     case FALSE:
  1022.         str = "FALSE";
  1023.         break;
  1024.  
  1025.     case ABSENT_BOOLEAN:
  1026.         str = "ABSENT_BOOLEAN";
  1027.         break;
  1028.  
  1029.     case CANCELLED_BOOLEAN:
  1030.         str = "CANCELLED_BOOLEAN";
  1031.         break;
  1032.     }
  1033.     (void) printf("\t/* %3d: %-8s */\t%s,\n",
  1034.               n, ExtBoolname(term, n, boolnames), str);
  1035.     }
  1036.     (void) printf("%s;\n", R_CURL);
  1037.  
  1038.     (void) printf("static short %s[] = %s\n", name_initializer("number"), L_CURL);
  1039.  
  1040.     for_each_number(n, term) {
  1041.     char buf[BUFSIZ];
  1042.     switch (term->Numbers[n]) {
  1043.     case ABSENT_NUMERIC:
  1044.         str = "ABSENT_NUMERIC";
  1045.         break;
  1046.     case CANCELLED_NUMERIC:
  1047.         str = "CANCELLED_NUMERIC";
  1048.         break;
  1049.     default:
  1050.         sprintf(buf, "%d", term->Numbers[n]);
  1051.         str = buf;
  1052.         break;
  1053.     }
  1054.     (void) printf("\t/* %3d: %-8s */\t%s,\n", n,
  1055.               ExtNumname(term, n, numnames), str);
  1056.     }
  1057.     (void) printf("%s;\n", R_CURL);
  1058.  
  1059.     size = sizeof(TERMTYPE)
  1060.     + (NUM_BOOLEANS(term) * sizeof(term->Booleans[0]))
  1061.     + (NUM_NUMBERS(term) * sizeof(term->Numbers[0]));
  1062.  
  1063.     (void) printf("static char * %s[] = %s\n", name_initializer("string"), L_CURL);
  1064.  
  1065.     for_each_string(n, term) {
  1066.  
  1067.     if (term->Strings[n] == ABSENT_STRING)
  1068.         str = "ABSENT_STRING";
  1069.     else if (term->Strings[n] == CANCELLED_STRING)
  1070.         str = "CANCELLED_STRING";
  1071.     else {
  1072.         str = string_variable(ExtStrname(term, n, strnames));
  1073.     }
  1074.     (void) printf("\t/* %3d: %-8s */\t%s,\n", n,
  1075.               ExtStrname(term, n, strnames), str);
  1076.     }
  1077.     (void) printf("%s;\n", R_CURL);
  1078.  
  1079. #if NCURSES_XNAMES
  1080.     if ((NUM_BOOLEANS(term) != BOOLCOUNT)
  1081.     || (NUM_NUMBERS(term) != NUMCOUNT)
  1082.     || (NUM_STRINGS(term) != STRCOUNT)) {
  1083.     (void) printf("static char * %s[] = %s\n",
  1084.               name_initializer("string_ext"), L_CURL);
  1085.     for (n = BOOLCOUNT; n < NUM_BOOLEANS(term); ++n) {
  1086.         (void) printf("\t/* %3d: bool */\t\"%s\",\n",
  1087.               n, ExtBoolname(term, n, boolnames));
  1088.     }
  1089.     for (n = NUMCOUNT; n < NUM_NUMBERS(term); ++n) {
  1090.         (void) printf("\t/* %3d: num */\t\"%s\",\n",
  1091.               n, ExtNumname(term, n, numnames));
  1092.     }
  1093.     for (n = STRCOUNT; n < NUM_STRINGS(term); ++n) {
  1094.         (void) printf("\t/* %3d: str */\t\"%s\",\n",
  1095.               n, ExtStrname(term, n, strnames));
  1096.     }
  1097.     (void) printf("%s;\n", R_CURL);
  1098.     }
  1099. #endif
  1100. }
  1101.  
  1102. /* dump C initializers for the terminal type */
  1103. static void
  1104. dump_termtype(TERMTYPE * term)
  1105. {
  1106.     (void) printf("\t%s\n\t\t%s,\n", L_CURL, name_initializer("alias"));
  1107.     (void) printf("\t\t(char *)0,\t/* pointer to string table */\n");
  1108.  
  1109.     (void) printf("\t\t%s,\n", name_initializer("bool"));
  1110.     (void) printf("\t\t%s,\n", name_initializer("number"));
  1111.  
  1112.     (void) printf("\t\t%s,\n", name_initializer("string"));
  1113.  
  1114. #if NCURSES_XNAMES
  1115.     (void) printf("#if NCURSES_XNAMES\n");
  1116.     (void) printf("\t\t(char *)0,\t/* pointer to extended string table */\n");
  1117.     (void) printf("\t\t%s,\t/* ...corresponding names */\n",
  1118.           ((NUM_BOOLEANS(term) != BOOLCOUNT)
  1119.            || (NUM_NUMBERS(term) != NUMCOUNT)
  1120.            || (NUM_STRINGS(term) != STRCOUNT))
  1121.           ? name_initializer("string_ext")
  1122.           : "(char **)0");
  1123.  
  1124.     (void) printf("\t\t%d,\t\t/* count total Booleans */\n", NUM_BOOLEANS(term));
  1125.     (void) printf("\t\t%d,\t\t/* count total Numbers */\n", NUM_NUMBERS(term));
  1126.     (void) printf("\t\t%d,\t\t/* count total Strings */\n", NUM_STRINGS(term));
  1127.  
  1128.     (void) printf("\t\t%d,\t\t/* count extensions to Booleans */\n",
  1129.           NUM_BOOLEANS(term) - BOOLCOUNT);
  1130.     (void) printf("\t\t%d,\t\t/* count extensions to Numbers */\n",
  1131.           NUM_NUMBERS(term) - NUMCOUNT);
  1132.     (void) printf("\t\t%d,\t\t/* count extensions to Strings */\n",
  1133.           NUM_STRINGS(term) - STRCOUNT);
  1134.  
  1135.     (void) printf("#endif /* NCURSES_XNAMES */\n");
  1136. #endif /* NCURSES_XNAMES */
  1137.     (void) printf("\t%s\n", R_CURL);
  1138. }
  1139.  
  1140. static int
  1141. optarg_to_number(void)
  1142. {
  1143.     char *temp = 0;
  1144.     long value = strtol(optarg, &temp, 0);
  1145.  
  1146.     if (temp == 0 || temp == optarg || *temp != 0) {
  1147.     fprintf(stderr, "Expected a number, not \"%s\"\n", optarg);
  1148.     exit(EXIT_FAILURE);
  1149.     }
  1150.     return (int) value;
  1151. }
  1152.  
  1153. /***************************************************************************
  1154.  *
  1155.  * Main sequence
  1156.  *
  1157.  ***************************************************************************/
  1158.  
  1159. int
  1160. main(int argc, char *argv[])
  1161. {
  1162.     char *terminal, *firstdir, *restdir;
  1163.     /* Avoid "local data >32k" error with mwcc */
  1164.     /* Also avoid overflowing smaller stacks on systems like AmigaOS */
  1165.     path *tfile = (path *) malloc(sizeof(path) * MAXTERMS);
  1166.     int c, i, len;
  1167.     bool formatted = FALSE;
  1168.     bool filecompare = FALSE;
  1169.     int initdump = 0;
  1170.     bool init_analyze = FALSE;
  1171.     bool suppress_untranslatable = FALSE;
  1172.  
  1173.     if ((terminal = getenv("TERM")) == 0) {
  1174.     (void) fprintf(stderr,
  1175.                "infocmp: environment variable TERM not set\n");
  1176.     return EXIT_FAILURE;
  1177.     }
  1178.  
  1179.     /* where is the terminfo database location going to default to? */
  1180.     restdir = firstdir = 0;
  1181.  
  1182.     while ((c = getopt(argc,
  1183.                argv,
  1184.                "1A:aB:CcdEeFfGgIiLlnpqR:rs:TtuVv:w:")) != EOF)
  1185.     switch (c) {
  1186.     case '1':
  1187.         mwidth = 0;
  1188.         break;
  1189.  
  1190.     case 'A':
  1191.         firstdir = optarg;
  1192.         break;
  1193.  
  1194. #if NCURSES_XNAMES
  1195.     case 'a':
  1196.         _nc_disable_period = TRUE;
  1197.         use_extended_names(TRUE);
  1198.         break;
  1199. #endif
  1200.     case 'B':
  1201.         restdir = optarg;
  1202.         break;
  1203.  
  1204.     case 'C':
  1205.         outform = F_TERMCAP;
  1206.         tversion = "BSD";
  1207.         if (sortmode == S_DEFAULT)
  1208.         sortmode = S_TERMCAP;
  1209.         break;
  1210.  
  1211.     case 'c':
  1212.         compare = C_COMMON;
  1213.         break;
  1214.  
  1215.     case 'd':
  1216.         compare = C_DIFFERENCE;
  1217.         break;
  1218.  
  1219.     case 'E':
  1220.         initdump |= 2;
  1221.         break;
  1222.  
  1223.     case 'e':
  1224.         initdump |= 1;
  1225.         break;
  1226.  
  1227.     case 'F':
  1228.         filecompare = TRUE;
  1229.         break;
  1230.  
  1231.     case 'f':
  1232.         formatted = TRUE;
  1233.         break;
  1234.  
  1235.     case 'G':
  1236.         numbers = 1;
  1237.         break;
  1238.  
  1239.     case 'g':
  1240.         numbers = -1;
  1241.         break;
  1242.  
  1243.     case 'I':
  1244.         outform = F_TERMINFO;
  1245.         if (sortmode == S_DEFAULT)
  1246.         sortmode = S_VARIABLE;
  1247.         tversion = 0;
  1248.         break;
  1249.  
  1250.     case 'i':
  1251.         init_analyze = TRUE;
  1252.         break;
  1253.  
  1254.     case 'L':
  1255.         outform = F_VARIABLE;
  1256.         if (sortmode == S_DEFAULT)
  1257.         sortmode = S_VARIABLE;
  1258.         break;
  1259.  
  1260.     case 'l':
  1261.         outform = F_TERMINFO;
  1262.         break;
  1263.  
  1264.     case 'n':
  1265.         compare = C_NAND;
  1266.         break;
  1267.  
  1268.     case 'p':
  1269.         ignorepads = TRUE;
  1270.         break;
  1271.  
  1272.     case 'q':
  1273.         quiet = TRUE;
  1274.         s_absent = "-";
  1275.         s_cancel = "@";
  1276.         bool_sep = ", ";
  1277.         break;
  1278.  
  1279.     case 'R':
  1280.         tversion = optarg;
  1281.         break;
  1282.  
  1283.     case 'r':
  1284.         tversion = 0;
  1285.         limited = FALSE;
  1286.         break;
  1287.  
  1288.     case 's':
  1289.         if (*optarg == 'd')
  1290.         sortmode = S_NOSORT;
  1291.         else if (*optarg == 'i')
  1292.         sortmode = S_TERMINFO;
  1293.         else if (*optarg == 'l')
  1294.         sortmode = S_VARIABLE;
  1295.         else if (*optarg == 'c')
  1296.         sortmode = S_TERMCAP;
  1297.         else {
  1298.         (void) fprintf(stderr,
  1299.                    "infocmp: unknown sort mode\n");
  1300.         return EXIT_FAILURE;
  1301.         }
  1302.         break;
  1303.  
  1304.     case 'T':
  1305.         limited = FALSE;
  1306.         break;
  1307.  
  1308. #if NCURSES_XNAMES
  1309.     case 't':
  1310.         _nc_disable_period = FALSE;
  1311.         suppress_untranslatable = TRUE;
  1312.         break;
  1313. #endif
  1314.  
  1315.     case 'u':
  1316.         compare = C_USEALL;
  1317.         break;
  1318.  
  1319.     case 'V':
  1320.         puts(curses_version());
  1321.         ExitProgram(EXIT_SUCCESS);
  1322.  
  1323.     case 'v':
  1324.         itrace = optarg_to_number();
  1325.         set_trace_level(itrace);
  1326.         break;
  1327.  
  1328.     case 'w':
  1329.         mwidth = optarg_to_number();
  1330.         break;
  1331.  
  1332.     default:
  1333.         usage();
  1334.     }
  1335.  
  1336.     /* by default, sort by terminfo name */
  1337.     if (sortmode == S_DEFAULT)
  1338.     sortmode = S_TERMINFO;
  1339.  
  1340.     /* set up for display */
  1341.     dump_init(tversion, outform, sortmode, mwidth, itrace, formatted);
  1342.  
  1343.     /* make sure we have at least one terminal name to work with */
  1344.     if (optind >= argc)
  1345.     argv[argc++] = terminal;
  1346.  
  1347.     /* if user is after a comparison, make sure we have two entries */
  1348.     if (compare != C_DEFAULT && optind >= argc - 1)
  1349.     argv[argc++] = terminal;
  1350.  
  1351.     /* exactly two terminal names with no options means do -d */
  1352.     if (argc - optind == 2 && compare == C_DEFAULT)
  1353.     compare = C_DIFFERENCE;
  1354.  
  1355.     if (!filecompare) {
  1356.     /* grab the entries */
  1357.     termcount = 0;
  1358.     for (; optind < argc; optind++) {
  1359.         if (termcount >= MAXTERMS) {
  1360.         (void) fprintf(stderr,
  1361.                    "infocmp: too many terminal type arguments\n");
  1362.         return EXIT_FAILURE;
  1363.         } else {
  1364.         const char *directory = termcount ? restdir : firstdir;
  1365.         int status;
  1366.  
  1367.         tname[termcount] = argv[optind];
  1368.  
  1369.         if (directory) {
  1370.             (void) sprintf(tfile[termcount], "%s/%c/%s",
  1371.                    directory,
  1372.                    *argv[optind], argv[optind]);
  1373.             if (itrace)
  1374.             (void) fprintf(stderr,
  1375.                        "infocmp: reading entry %s from file %s\n",
  1376.                        argv[optind], tfile[termcount]);
  1377.  
  1378.             status = _nc_read_file_entry(tfile[termcount],
  1379.                          &entries[termcount].tterm);
  1380.         } else {
  1381.             if (itrace)
  1382.             (void) fprintf(stderr,
  1383.                        "infocmp: reading entry %s from system directories %s\n",
  1384.                        argv[optind], tname[termcount]);
  1385.  
  1386.             status = _nc_read_entry(tname[termcount],
  1387.                         tfile[termcount],
  1388.                         &entries[termcount].tterm);
  1389.             directory = TERMINFO;    /* for error message */
  1390.         }
  1391.  
  1392.         if (status <= 0) {
  1393.             (void) fprintf(stderr,
  1394.                    "infocmp: couldn't open terminfo file %s.\n",
  1395.                    tfile[termcount]);
  1396.             return EXIT_FAILURE;
  1397.         }
  1398.         repair_acsc(&entries[termcount].tterm);
  1399.         termcount++;
  1400.         }
  1401.     }
  1402.  
  1403. #if NCURSES_XNAMES
  1404.     if (termcount > 1)
  1405.         _nc_align_termtype(&entries[0].tterm, &entries[1].tterm);
  1406. #endif
  1407.  
  1408.     /* dump as C initializer for the terminal type */
  1409.     if (initdump) {
  1410.         if (initdump & 1)
  1411.         dump_termtype(&entries[0].tterm);
  1412.         if (initdump & 2)
  1413.         dump_initializers(&entries[0].tterm);
  1414.         ExitProgram(EXIT_SUCCESS);
  1415.     }
  1416.  
  1417.     /* analyze the init strings */
  1418.     if (init_analyze) {
  1419. #undef CUR
  1420. #define CUR    entries[0].tterm.
  1421.         analyze_string("is1", init_1string, &entries[0].tterm);
  1422.         analyze_string("is2", init_2string, &entries[0].tterm);
  1423.         analyze_string("is3", init_3string, &entries[0].tterm);
  1424.         analyze_string("rs1", reset_1string, &entries[0].tterm);
  1425.         analyze_string("rs2", reset_2string, &entries[0].tterm);
  1426.         analyze_string("rs3", reset_3string, &entries[0].tterm);
  1427.         analyze_string("smcup", enter_ca_mode, &entries[0].tterm);
  1428.         analyze_string("rmcup", exit_ca_mode, &entries[0].tterm);
  1429. #undef CUR
  1430.         ExitProgram(EXIT_SUCCESS);
  1431.     }
  1432.  
  1433.     /*
  1434.      * Here's where the real work gets done
  1435.      */
  1436.     switch (compare) {
  1437.     case C_DEFAULT:
  1438.         if (itrace)
  1439.         (void) fprintf(stderr,
  1440.                    "infocmp: about to dump %s\n",
  1441.                    tname[0]);
  1442.         (void) printf("#\tReconstructed via infocmp from file: %s\n",
  1443.               tfile[0]);
  1444.         len = dump_entry(&entries[0].tterm,
  1445.                  suppress_untranslatable,
  1446.                  limited,
  1447.                  0,
  1448.                  numbers,
  1449.                  NULL);
  1450.         putchar('\n');
  1451.         if (itrace)
  1452.         (void) fprintf(stderr, "infocmp: length %d\n", len);
  1453.         break;
  1454.  
  1455.     case C_DIFFERENCE:
  1456.         if (itrace)
  1457.         (void) fprintf(stderr, "infocmp: dumping differences\n");
  1458.         (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
  1459.         compare_entry(compare_predicate, &entries->tterm, quiet);
  1460.         break;
  1461.  
  1462.     case C_COMMON:
  1463.         if (itrace)
  1464.         (void) fprintf(stderr,
  1465.                    "infocmp: dumping common capabilities\n");
  1466.         (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
  1467.         compare_entry(compare_predicate, &entries->tterm, quiet);
  1468.         break;
  1469.  
  1470.     case C_NAND:
  1471.         if (itrace)
  1472.         (void) fprintf(stderr,
  1473.                    "infocmp: dumping differences\n");
  1474.         (void) printf("comparing %s to %s.\n", tname[0], tname[1]);
  1475.         compare_entry(compare_predicate, &entries->tterm, quiet);
  1476.         break;
  1477.  
  1478.     case C_USEALL:
  1479.         if (itrace)
  1480.         (void) fprintf(stderr, "infocmp: dumping use entry\n");
  1481.         len = dump_entry(&entries[0].tterm,
  1482.                  suppress_untranslatable,
  1483.                  limited,
  1484.                  0,
  1485.                  numbers,
  1486.                  use_predicate);
  1487.         for (i = 1; i < termcount; i++)
  1488.         len += dump_uses(tname[i], !(outform == F_TERMCAP
  1489.                          || outform == F_TCONVERR));
  1490.         putchar('\n');
  1491.         if (itrace)
  1492.         (void) fprintf(stderr, "infocmp: length %d\n", len);
  1493.         break;
  1494.     }
  1495.     } else if (compare == C_USEALL)
  1496.     (void) fprintf(stderr, "Sorry, -u doesn't work with -F\n");
  1497.     else if (compare == C_DEFAULT)
  1498.     (void) fprintf(stderr, "Use `tic -[CI] <file>' for this.\n");
  1499.     else if (argc - optind != 2)
  1500.     (void) fprintf(stderr,
  1501.                "File comparison needs exactly two file arguments.\n");
  1502.     else
  1503.     file_comparison(argc - optind, argv + optind);
  1504.  
  1505.     ExitProgram(EXIT_SUCCESS);
  1506. }
  1507.  
  1508. /* infocmp.c ends here */
  1509.